package com.jb.charts;

import com.jb.util.ClassUtil;

import com.jb.web.bean.UIComponentImpl;
import com.jb.web.bean.inspector.OptionPropertyEditor;
import com.jb.web.renderer.RenderKitImpl;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Point2D;

import java.io.FileOutputStream;

import javax.servlet.http.HttpServletResponse;

import org.jCharts.Chart;
import org.jCharts.axisChart.AxisChart;
import org.jCharts.axisChart.ScatterPlotAxisChart;
import org.jCharts.chartData.AxisChartDataSet;
import org.jCharts.chartData.ChartDataException;
import org.jCharts.chartData.DataSeries;
import org.jCharts.chartData.PieChartDataSet;
import org.jCharts.chartData.ScatterPlotDataSeries;
import org.jCharts.chartData.ScatterPlotDataSet;
import org.jCharts.chartData.interfaces.IAxisDataSeries;
import org.jCharts.encoders.JPEGEncoder;
import org.jCharts.encoders.ServletEncoderHelper;
import org.jCharts.nonAxisChart.PieChart2D;
import org.jCharts.properties.AreaChartProperties;
import org.jCharts.properties.AxisChartTypeProperties;
import org.jCharts.properties.AxisProperties;
import org.jCharts.properties.AxisTypeProperties;
import org.jCharts.properties.BarChartProperties;
import org.jCharts.properties.ChartProperties;
import org.jCharts.properties.ClusteredBarChartProperties;
import org.jCharts.properties.DataAxisProperties;
import org.jCharts.properties.LegendProperties;
import org.jCharts.properties.LineChartProperties;
import org.jCharts.properties.PieChart2DProperties;
import org.jCharts.properties.PointChartProperties;
import org.jCharts.properties.ScatterPlotProperties;
import org.jCharts.properties.StackedBarChartProperties;
import org.jCharts.properties.util.ChartFont;
import org.jCharts.properties.util.ChartStroke;
import org.jCharts.types.ChartType;


public class ChartImpl extends UIComponentImpl {
    static final long serialVersionUID = 123123L;
    public static String CHART = "Chart";
    
    static {
        publish(ChartImpl.class, GENERAL, "title");
        publish(ChartImpl.class, CHART, "titleX");
        publish(ChartImpl.class, CHART, "titleY");
        publish(ChartImpl.class, CHART, "model");
        publish(ChartImpl.class, CHART, "round");
        //
        publish(ChartImpl.class, CHART, "stack");
        //
     //   publish(ChartImpl.class, CHART, "cluster");

        publish(ChartImpl.class, CHART, "showLegend",
                new OptionPropertyEditor("seed:Boolean"));
        publish(ChartImpl.class, GENERAL, "cached",
                new OptionPropertyEditor("seed:Boolean"));

        publish(ChartImpl.class, CHART, "chartWidth");
        publish(ChartImpl.class, CHART, "chartHeight");
        publish(ChartImpl.class, CHART, "chartType", 
                new OptionPropertyEditor("static:0,Line;1,Area;2,Bar Chart;3,Pie Chart"));
        RenderKitImpl.registerRenderer(ChartImpl.class, ChartRenderer.class);
    }

    private String title;
    private String titleX;
    private String titleY;
    private String chartType = "0";
    private boolean cached;
    private int round=0;
    private boolean showLegend;
    private Stroke[] strokes;
    private Shape[] shapes;
    private ChartModel cm;
    private String[] legendLabels;
    private Paint[] paints;
    
    private LegendProperties legendProperties = new LegendProperties();
    private ChartProperties chartProperties = new ChartProperties();
    private AxisProperties axisProperties = new AxisProperties(false);

    private String model = DummyChartModel.class.getName();
  
    private int chartWidth = 500;
    private int chartHeight = 350;

    private Color[] colors= new Color[]{
    new Color(255,123,13),
    new Color(115,123,33),
    new Color(55,123,33),
    new Color(155,223,133),
        Color.black,                     
        Color.lightGray,
        Color.gray,
        Color.magenta,
        Color.pink, 
        Color.cyan,
        Color.orange               
    };
    
    private boolean stack;
  //  private boolean cluster;

    private ChartType getType() {
        int ct = Integer.parseInt(chartType);
        switch (ct) {
        case 0:
            return ChartType.LINE;
        case 1:
            if(stack)
                return ChartType.AREA_STACKED;
            else
                return ChartType.AREA;
        case 2:
            if(stack)
                return ChartType.BAR_STACKED;
            else
                return ChartType.BAR_CLUSTERED;
        case 3:
            return ChartType.POINT;
        case 4:
            return ChartType.SCATTER_PLOT;
        case 5:
            return ChartType.STOCK;
        default:
            return ChartType.LINE;
        }
    }

    private Chart getChart() {
        updateModel();
        int ct = Integer.parseInt(chartType);
        switch (ct) {
        case 0:
            return line();
        case 1:
            return area();
        case 2:
            return bar();
        case 3:
            return pie();
        case 4:
            return scatter();
        case 5:
            return line();
        default:
            return line();
        }
    }
    private Paint[] getPaint(int count) {
        Paint[] ps=new Paint[count];
        int j=0;
        for(int i=0; i<count;i++) {
            ps[i]=colors[j];
            if(j<colors.length-1)
                j++;
            else
                j=0;
                    
        }
        return ps;
    }
    
    public final BasicStroke stroke=new BasicStroke(0.2f,
                   BasicStroke.CAP_BUTT,
                   BasicStroke.JOIN_ROUND,
                   1.5f,
                   new float[] {0.1f},
                   0.4f);
    public final BasicStroke lineStroke=new BasicStroke(1.2f,
                   BasicStroke.CAP_BUTT,
                   BasicStroke.JOIN_ROUND,
                   1.5f,
                   new float[] {0.1f},
                   0.4f);
    
    ChartStroke cs = new ChartStroke(stroke, Color.black);
    
    private ScatterPlotProperties createScatterPlotProperties() {
        strokes = new Stroke[] { stroke};
        

        shapes = new Shape[] { PointChartProperties.SHAPE_CIRCLE };

        return new ScatterPlotProperties(strokes, shapes);
    }


    /*****************************************************************************************
     * Generates a random MultiDataSet
     *
     * @return ScatterPlotDataSet
     ******************************************************************************************/
    private ScatterPlotDataSet createScatterPlotDataSet() {
        Point2D.Double[] points = new Point2D.Double[20];
        for (int x = 0; x < 20; x++) {
            //--- y = x^2
            points[x] = ScatterPlotDataSet.createPoint2DDouble();
            points[x].setLocation(x, Math.pow(x, 2));
        }

        ScatterPlotDataSet scatterPlotDataSet = 
            new ScatterPlotDataSet(this.createScatterPlotProperties());
        scatterPlotDataSet.addDataPoints(points, Color.red, "Proprietary");

        return scatterPlotDataSet;
    }
    
    public Chart scatter() {
        try {
            ScatterPlotDataSet scatterPlotDataSet = 
                this.createScatterPlotDataSet();
            ScatterPlotDataSeries scatterPlotDataSeries = 
                new ScatterPlotDataSeries(scatterPlotDataSet, testString(titleX), testString(titleY), testString(getTitle()));

            DataAxisProperties xAxisProperties = new DataAxisProperties();
            xAxisProperties.setUserDefinedScale(-5, 3);
            xAxisProperties.setNumItems(10);
            xAxisProperties.setRoundToNearest(0);

            DataAxisProperties yAxisProperties = new DataAxisProperties();
            yAxisProperties.setUserDefinedScale(-30, 50);
            yAxisProperties.setNumItems(10);
            yAxisProperties.setRoundToNearest(1);

            AxisProperties axisProperties = new AxisProperties(xAxisProperties, yAxisProperties);
           
            return new ScatterPlotAxisChart(scatterPlotDataSeries, chartProperties, axisProperties, legendProperties, 500, 400);

        } catch (Throwable throwable) {
            //HACK do your error handling here...
            throwable.printStackTrace();
        }
        return null;

    }

    void updateModel() {
        try {
            cm = (ChartModel)ClassUtil.createInstance(model);
            int series = cm.getSeries();
            strokes = new Stroke[series];
            shapes = new Shape[series];
            for (int i = 0; i < series; i++) {
                strokes[i] = lineStroke;//LineChartProperties.DEFAULT_LINE_STROKE;
                shapes[i] = null;//PointChartProperties.SHAPE_TRIANGLE;
            }
           
            legendLabels = cm.getLegends();

            if(!showLegend) {
                legendProperties=null;
            }else
                legendProperties = new LegendProperties();
            
            paints = getPaint(cm.getSeries());     
            
            ChartFont axisScaleFont = new ChartFont(new Font("Arial Narrow", Font.PLAIN, 10), Color.red);    
            AxisTypeProperties x=axisProperties.getXAxisProperties();
            AxisTypeProperties y=axisProperties.getYAxisProperties();
            x.setScaleChartFont(axisScaleFont);
            y.setScaleChartFont(axisScaleFont);

            DataAxisProperties dataAxisProperties= (DataAxisProperties)y;
            //scale
            dataAxisProperties.setRoundToNearest(round);
            
                
            y.setAxisStroke(cs);
            y.setTickChartStroke(cs);
            x.setAxisStroke(cs);
            x.setTickChartStroke(cs);
             
            ChartFont axisTitleFont = new ChartFont(new Font("Arial Narrow", Font.PLAIN, 10), Color.red);
            x.setTitleChartFont(axisTitleFont);
            y.setTitleChartFont(axisTitleFont);            
        } catch (Exception e) {
            e.printStackTrace();        
        }
    }
    String testString(String str) {
        return str==null||str.length()==0?null:str;
    }
    
    private BarChartProperties getBarChartProperties() {
       
        if(stack)
            return new StackedBarChartProperties();
        else
            return new ClusteredBarChartProperties();
    }
    
    private Chart bar() {
        BarChartProperties barChartProperties = getBarChartProperties();
        try {
            
            IAxisDataSeries dataSeries = new DataSeries(cm.getXLabels(), testString(titleX), testString(titleY), testString(getTitle()));
            double[][] data = cm.getData();
            barChartProperties.setBarOutlineStroke(cs);
            dataSeries.addIAxisPlotDataSet(
                new AxisChartDataSet(data, legendLabels, paints, getType(), barChartProperties));
            return new AxisChart(dataSeries, chartProperties, axisProperties, legendProperties, chartWidth, chartHeight);
        } catch (Throwable throwable) {
            //HACK do your error handling here...
            throwable.printStackTrace();
        }
        return null;
    }

    public Chart pie() {
        PieChart2DProperties properties= new PieChart2DProperties();
        if(legendProperties!=null)
        {
            legendProperties.setNumColumns(2);
            legendProperties.setPlacement(LegendProperties.RIGHT);
        }
        
        try {
            properties.setBorderStroke(stroke);
            properties.setBorderPaint(Color.blue);

            return new PieChart2D(getData(properties), legendProperties, 
                                  chartProperties, chartWidth, 
                                  chartHeight);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
    
    public static void main(String[] args) {
        ChartImpl impl = new ChartImpl();
        impl.setChartType("0");
        Chart chart = impl.getChart();

        try {
            JPEGEncoder.encode(chart, 1.0f, new FileOutputStream("c:/test.jpg"));
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
    
    private PieChartDataSet getData(PieChart2DProperties properties) throws ChartDataException {
        double[][] data = cm.getData();//only one dimension
        paints = getPaint(data[0].length);
        return new PieChartDataSet(testString(getTitle()), data[0], cm.getXLabels(), paints, properties);
    }
    
    public Chart line() {
        try {
            LineChartProperties lineChartProperties = new LineChartProperties(strokes, shapes);
            //AxisChartTypeProperties typeProp = new AxisChartTypeProperties();
            DataSeries dataSeries = new DataSeries(cm.getXLabels(),testString(titleX), testString(titleY), testString(getTitle()));
            AxisChartDataSet acds = new AxisChartDataSet(cm.getData(), legendLabels, paints, getType(), lineChartProperties);
            dataSeries.addIAxisPlotDataSet(acds);
            return new AxisChart(dataSeries, chartProperties, axisProperties, legendProperties, chartWidth, chartHeight);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return null;
    }

    public Chart area() {
        try {

            AxisChartTypeProperties chartTypeProperties = new AreaChartProperties();
            DataSeries dataSeries = new DataSeries(cm.getXLabels(),testString(titleX), testString(titleY), testString(getTitle()));
            AxisChartDataSet acds = new AxisChartDataSet(cm.getData(), legendLabels, paints, getType(), chartTypeProperties);
            dataSeries.addIAxisPlotDataSet(acds);
            
            return new AxisChart(dataSeries, chartProperties, axisProperties, legendProperties, chartWidth, chartHeight);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return null;
    }

    public void generate(HttpServletResponse response) {
        try {
            Chart chart = getChart();
            if(chart!=null)
                ServletEncoderHelper.encodeJPEG13(chart, 1.0f, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setChartWidth(int param) {
        this.chartWidth = param;
    }

    public int getChartWidth() {
        return chartWidth;
    }

    public void setChartHeight(int param) {
        this.chartHeight = param;
    }

    public int getChartHeight() {
        return chartHeight;
    }

    public void setModel(String param) {
        this.model = param;
    }

    public String getModel() {
        return model;
    }

    public void setTitleX(String param) {
        this.titleX = param;
    }

    public String getTitleX() {
        return titleX;
    }

    public void setTitleY(String param) {
        this.titleY = param;
    }

    public String getTitleY() {
        return titleY;
    }

    public void setChartType(String param) {
        this.chartType = param;
    }

    public String getChartType() {
        return chartType;
    }

    public void setRound(int param) {
        this.round = param;
    }
    public int getRound() {
        return round;
    }
    public void setShowLegend(boolean param) {
        this.showLegend = param;
    }
    public boolean isShowLegend() {
        return showLegend;
    }
    public void setStack(boolean param) {
        this.stack = param;
    }
    public boolean isStack() {
        return stack;
    }
    
    public void setCached(boolean param) {
        this.cached = param;
    }

    public boolean isCached() {
        return cached;
    }

    public void setTitle(String param) {
        this.title = param;
    }

    public String getTitle() {
        return title;
    }
}
